/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.common.cache.DictCache;
import org.springblade.common.utils.CommonUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springblade.modules.admin.entity.ElevatorStopLog;
import org.springblade.modules.admin.entity.MaintainPlanContent;
import org.springblade.modules.admin.enums.ElevatorStatusEnum;
import org.springblade.modules.admin.enums.ElevatorStopTypeEnum;
import org.springblade.modules.admin.enums.MaintainPlanContentEnum;
import org.springblade.modules.admin.enums.SourceEnum;
import org.springblade.modules.admin.mapper.ElevatorStopLogMapper;
import org.springblade.modules.admin.mapper.MaintainPlanContentMapper;
import org.springblade.modules.admin.service.IElevatorStopLogService;
import org.springblade.modules.admin.service.IMaintainPlanContentService;
import org.springblade.modules.admin.utils.DateUtils;
import org.springblade.modules.admin.vo.ElevatorStopLogListVO;
import org.springblade.modules.admin.vo.ElevatorStopLogVO;
import org.springblade.modules.admin.vo.plan.MaintainPlanBaseInfoVO;
import org.springblade.modules.system.entity.Dict;
import org.springblade.modules.system.service.IDictService;
import org.springblade.modules.system.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 电梯停梯状态日志 服务实现类
 *
 * @author ysq
 * @since 2022-10-09
 */
@Service
public class ElevatorStopLogServiceImpl extends BaseServiceImpl<ElevatorStopLogMapper, ElevatorStopLog> implements IElevatorStopLogService {

	@Autowired
	private IDictService DictService;
	@Autowired
	private IUserService userService;
	@Autowired
	@Lazy
	private IMaintainPlanContentService planContentService;

	@Override
	public IPage<ElevatorStopLogVO> selectElevatorStopLogPage(IPage<ElevatorStopLogVO> page, ElevatorStopLogVO elevatorStopLog) {
		return page.setRecords(baseMapper.selectElevatorStopLogPage(page, elevatorStopLog));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean insertStopLog(ElevatorStopLog elevatorStopLog) {
		if(Func.isEmpty(elevatorStopLog.getStopType())){
			if(Func.isNotEmpty(elevatorStopLog.getModule())){
				MaintainPlanContent planContent = planContentService.getById(elevatorStopLog.getModule());
				elevatorStopLog.setStopType(ElevatorStopTypeEnum.getIdByPlanType(planContent.getTypeStatus()));
			}else {
				elevatorStopLog.setStopType(ElevatorStopTypeEnum.ELEVATOR_NON_FAULT.getId());
			}
		}
		// 最新一次状态变更记录
		ElevatorStopLog latestLog = baseMapper.getLatestLog(elevatorStopLog.getElevatorId());
		boolean changeFlag = false;
		if (Func.isNotEmpty(latestLog)) {
			changeFlag = true;
			if (Func.equalsSafe(elevatorStopLog.getSetStatus(), latestLog.getSetStatus())) {
				log.warn("停梯状态已变更，无法重复修改");
				return false;
			}
		}

		if (ElevatorStatusEnum.STOP.getStatus() == elevatorStopLog.getSetStatus()) {
			elevatorStopLog.setStopReasonAncestors(this.getDictStopReasonAncestors(elevatorStopLog.getStopReason()));
			elevatorStopLog.setStopReasonName(DictCache.getValue(STOP_ELEVATOR_STATUS_DICT_KEY, elevatorStopLog.getStopReason()));
		}
		elevatorStopLog.setSource(SourceEnum.MANAGEMENT_SIDE.getId());
		elevatorStopLog.setPlatform(0);
		if (this.save(elevatorStopLog)) {
			if (changeFlag) {
				// 耗时
				Duration duration = DateUtil.between(latestLog.getCreateTime(), elevatorStopLog.getCreateTime());
				// 更新上个状态耗时
				latestLog.setDuration(duration.getSeconds());
				int version = latestLog.getVersion();
				latestLog.setVersion(version + 1);
				this.update(latestLog, Wrappers.<ElevatorStopLog>lambdaUpdate().eq(ElevatorStopLog :: getId, latestLog.getId()).eq(ElevatorStopLog :: getVersion, version));
			}
			return true;
		}

		return false;
	}


	/**
	 * 查询停梯原因key祖级列表
	 * @param dictKey
	 * @return
	 */
	private String getDictStopReasonAncestors(String dictKey) {
		if (Func.isBlank(dictKey)) {
			return null;
		}

		LinkedList<String> ancestors = new LinkedList<>();
		Dict dict = DictService.getOne(Wrappers.<Dict>lambdaQuery().eq(Dict :: getCode, STOP_ELEVATOR_STATUS_DICT_KEY).eq(Dict::getDictKey, dictKey), false);
		if (Func.isNotEmpty(dict)) {
			List<Dict> dictList = DictCache.getList(STOP_ELEVATOR_STATUS_DICT_KEY);
			Map<Long, Dict> map = dictList.stream().collect(Collectors.toMap(t -> t.getId(), t -> t));
			Dict parentDict = map.get(dict.getParentId());
			while (Func.isNotEmpty(parentDict)) {
				ancestors.add(parentDict.getDictKey());
				parentDict = map.get(parentDict.getParentId());
			}
		}
		// 倒序
		Collections.reverse(ancestors);
		return Func.isEmpty(ancestors) ? null : ancestors.stream().collect(Collectors.joining(","));
	}

	/**
	 * 根据 电梯Id 查询 单台梯停梯日志
	 */
	@Override
	public IPage<ElevatorStopLogVO> selectStopLogPageById(IPage<ElevatorStopLogVO> page, Long elevatorId, String beginTime, String endTime, List<Long> userIds, Integer source, Integer module) {
		IPage<ElevatorStopLogVO> elevatorStopLogVOIPage = baseMapper.selectLogPageById(page, elevatorId, beginTime, endTime,userIds,source,module);
		//修正 结果中module参数的格式，停梯时长计算
		List<ElevatorStopLogVO> tempList = elevatorStopLogVOIPage.getRecords();
		if(Func.isNotEmpty(tempList)){
			for(ElevatorStopLogVO temp : tempList){
				String planId = temp.getModule();
				temp.setModuleName("电梯运行管理");
				if(Func.isNotEmpty(planId)){
					String replace = planId.replace("'", "");
					temp.setModule(replace);
					//根据 module 查询 停梯状态
					long contentId = Long.parseLong(replace);
					Integer typeStatus =  baseMapper.getContentTypeStatus(contentId);
					temp.setTypeStatus(typeStatus);

					if(Func.isNotEmpty(typeStatus)){
						temp.setModuleName(MaintainPlanContentEnum.getName(typeStatus));
					}
				}
				// 当停梯状态： 1 —> 2 时，时间返回-1
				if(1 == temp.getInitStatus() && 2 == temp.getSetStatus()){
					temp.setDuration(-1L);
				}

				//增加操作人
				Long createUserId = temp.getCreateUser();
				if(Func.isNull(createUserId)){
					temp.setCreateUserName(null);
				}else{
					String realName = userService.getById(createUserId).getRealName();
					temp.setCreateUserName(realName);
				}
			}
		}

		if(Func.isNotEmpty(tempList)){
			//计算停梯时间
			for(int i = 0; i < tempList.size()-1; i++){
				if(2==tempList.get(i).getInitStatus() && 1==tempList.get(i).getSetStatus()) {
					//获取数据并计算差值
					ElevatorStopLogVO startVO = tempList.get(i+1);
					ElevatorStopLogVO endVO = tempList.get(i);
					long start = startVO.getCreateTime().getTime();
					long end = endVO.getCreateTime().getTime();
					long difference = end - start;
					endVO.setDuration(difference);
					tempList.set(i,endVO);
				}
			}
		}
		// 停梯时间格式化
		if(Func.isNotEmpty(tempList)){
			for(ElevatorStopLogVO temp : tempList){
				if(-1 == temp.getDuration()){
					temp.setStopTime("--");
				}else {
					long minutes = temp.getDuration() / 1000 / 60;
					long seconds = temp.getDuration() /1000 % 60;
					temp.setStopTime(minutes+"分钟"+seconds+"秒");
				}
			}
		}
		elevatorStopLogVOIPage.setRecords(tempList);
		return elevatorStopLogVOIPage;
	}

	@Override
	public IPage<ElevatorStopLogListVO> selectElevatorPage(ElevatorStopLogListVO vo, IPage<ElevatorStopLogListVO> page) {
		IPage<ElevatorStopLogListVO> result = page.setRecords(baseMapper.selectElevatorPage(vo,page));
		result.getRecords().forEach(e -> {
			e.setWeekDurationStr(DateUtils.millisecondToTime(e.getRealTimeDuration()+e.getWeekDuration()));
			e.setMonthDurationStr(DateUtils.millisecondToTime(e.getRealTimeDuration()+e.getMonthDuration()));
		});
		return result;
	}

	@Override
	public ElevatorStopLog getLatestLog(Long elevatorId) {
		return baseMapper.getLatestLog(elevatorId);
	}

	@Override
	public void downExcel(ElevatorStopLogListVO vo, HttpServletResponse response) throws IOException {

		List<ElevatorStopLogListVO> list = baseMapper.selectElevatorList(vo);

		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			LinkedList<List<String>> rows = new LinkedList<>();
			List<String> title = CollUtil.newArrayList("项目名称", "电梯位置", "电梯实时状态", "变更时间", "实时停梯时长"
				, "停梯类型", "停梯原因", "近7天累计停梯时长", "近30天累计停梯时长", "近7天累计停梯次数", "近30天累计停梯次数", "操作渠道", "操作模块"
				, "报障状态", "报障时间", "项目是否多梯", "是否驻点", "项目所属区域", "项目类型", "项目地址");
			if(Func.isNotEmpty(list)){
				// 封装导出数据
				for (ElevatorStopLogListVO v : list) {
					List<String> row = new ArrayList<>(8);
					row.add(v.getBuildingName());
					row.add(v.getElevatorAddress());
					row.add(ElevatorStatusEnum.getName(v.getIsStop()));
					row.add(Func.isNotEmpty(v.getCreateTime()) ? DateUtil.format(v.getCreateTime(),"yyyy-MM-dd HH:mm:ss") : "");
					row.add(v.getRealTimeDurationStr());
					row.add(v.getStopTypeName());
					row.add(v.getStopTypeDesc());
					row.add(v.getWeekDurationStr());
					row.add(v.getMonthDurationStr());
					row.add(Convert.toStr(v.getWeekCount(),"0"));
					row.add(Convert.toStr(v.getMonthCount(),"0"));
					row.add(v.getSourceName());
					row.add(v.getModuleName());
					row.add(Func.isNotEmpty(v.getFaultElevatorStop()) ? ElevatorStatusEnum.getName(v.getFaultElevatorStop()) : "");
					row.add(Func.isNotEmpty(v.getFaultCreateTime()) ? DateUtil.format(v.getCreateTime(),"yyyy-MM-dd HH:mm:ss") : "");
					row.add(v.getElevatorCount() > 1 ? "多梯" : "单梯");
					if(Func.isNotEmpty(v.getStagnationPoint())){
						row.add(v.getStagnationPoint() == 1 ? "否" : "是");
					}else {
						row.add("");
					}
					row.add(v.getAdName());
					row.add(v.getBuildingTypeValue());
					row.add(v.getBuildingAddress());

					rows.add(row);
				}
			}
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.setColumnWidth(0, 30);
			excelWriter.setColumnWidth(1, 30);
			excelWriter.setColumnWidth(2, 30);
			excelWriter.setColumnWidth(3, 30);
			excelWriter.setColumnWidth(4, 30);
			excelWriter.setColumnWidth(5, 30);
			excelWriter.setColumnWidth(6, 30);
			excelWriter.setColumnWidth(7, 30);
			excelWriter.setColumnWidth(8, 30);
			excelWriter.setColumnWidth(9, 30);
			excelWriter.setColumnWidth(10, 30);
			excelWriter.setColumnWidth(11, 30);
			excelWriter.setColumnWidth(12, 30);
			excelWriter.setColumnWidth(13, 30);
			excelWriter.setColumnWidth(14, 30);
			excelWriter.setColumnWidth(15, 30);
			excelWriter.setColumnWidth(16, 30);
			excelWriter.setColumnWidth(17, 30);
			excelWriter.setColumnWidth(18, 30);
			excelWriter.setColumnWidth(19, 30);

			// 单独设置头部
			excelWriter.writeHeadRow(title);
			String fileName = "电梯运行状态_" + cn.hutool.core.date.DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			CommonUtil.setResponseHeader(response, fileName);
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}

	}


}
